/* 算法实现模板 */
import AlgorithmBase from "./AlgorithmBase";
import { createKDNode, NodeRange, calcNodeRange, KDNode } from "../../common/KDTreeFuncs";
import DefaultValue from "./input/TreePruning.input.json";
import DefaultValueTest from "./input/KDTreeFindNearest.input.json";
import Vector, { toVector } from "../../common/Vector";
import { showMathStringInTable } from "../../visual/Table2D";
import { drawDecisionTree } from "../../visual/Tree2D";
class DecisionTreeID3 extends AlgorithmBase{
  constructor(){
    super('DecisionTreeID3');
    this.id = 'DecisionTreeID3';
    this.DefaultValue = DefaultValueTest;
  }
  init(){
    
    // this.trainDS= 'd'
   
    var dataSet = DefaultValue.TrainDS.dataSet
    var label = DefaultValue.TrainDS.label
    var root = new treeElement() 
    this.loadModel(this.DefaultValue)
    
    
    this.drawtree =[]
    this.step =1
    
    
  

    this.print();
    // console.log(this.outputMap(this.createNewTree(root,dataSet,label)))
    console.log(this.drawtree);
  }


  createNewTree(root,dataSet,label){
    var entropyList =[]
    console.log(this.initEntropy(dataSet));

    for(let i = 0;i<dataSet[0].length-1;i++){
      entropyList.push(this.everyEntropy(dataSet,i))
      if(this.everyEntropy(dataSet,i)!=0){ 
        var dl = new drawlist(label[i],this.everyEntropy(dataSet,i),this.step)
        this.drawtree.push(dl)
      }
    }
    this.step++
    var dataob = this.sortData(dataSet,entropyList,label)
    console.log(dataob.dataSet);
    console.log(dataob.label);
    var datasetandsplit = this.dealdata(dataob.dataSet,dataob.label)
    
    root.name = dataob.label[0]
    root.featureList =dataob.hash
    root.split = datasetandsplit.split
    if(datasetandsplit.dataset.length==0)
      {
      console.log('分类结束');
      return new treeElement(dataob.hash,dataob.hash,dataob.label[0],root,null,0)
      }
    else{
      root.children = new treeElement()
      root.children = this.createNewTree(root.children,dataSet,label)
      return root
    }
  
  }

  

  //输出拿到treeElement后Map
  outputMap(tree){
    console.log(tree);
    var map = {}
    map['node'] = tree.name
    switch(tree.split){
      case 0:
        map['有'] = {node:'同意'}
        map['没有'] = {node:'拒绝'}
        return map
        break;
      case 1:
        map['有'] = {node:'同意'}
        map['没有'] = this.outputMap(tree.children)
        return map
        break;
      case 2:
        map['有'] = this.outputMap(tree.children)
        map['没有'] = {node:'拒绝'}
        return map
        break;
      case 3:
        map['有'] = this.outputMap(tree.children)
        map['没有'] = this.outputMap(tree.children)
        return map
        break;
    }
  
  }

  //计算初始熵
  initEntropy(data){
    var tNumber = 0 
    var fNumber = 0
    for (let i = 0; i < data.length;i++ ) {
      if(data[i].slice(-1)=="同意")
        tNumber++
      else{
        fNumber++
      }      
    }
    var pt = tNumber/data.length
    var pf = fNumber/data.length
    var ht = pt*Math.log2(1/pt) + pf*Math.log2(1/pf)
    this.tNumber =tNumber
    this.fNumber =fNumber
    this.ht = ht
    return ht
  }

  //计算增益值
  everyEntropy(data,index){
    var list = []
    var hash =[];
    var res = 0
    var datat = this.getdatat(data)
    //datat[index]为所求特征的索引  hasb为所求特征的属性值
    for (var i = 0; i < datat[index].length; i++) {
      if(hash.indexOf(datat[index][i])==-1){
      hash.push(datat[index][i]);
      } 
    }

  for(let i =0;i<hash.length;i++){
    var snum = 0
    var num = 0
    for(let j=0 ;j<data.length;j++){
      if(data[j][index]===hash[i]&&data[j].slice(-1)=="同意"){
        num++   
      }
      if(data[j][index]===hash[i]){
        snum++
      }
    }
    
    var p1 = snum/data.length
    var p = num/snum
    var pl = p*Math.log2(1/(p))
    var pr = (1-p)*Math.log2(1/(1-p))
    
   
    if(p==0)
      pl = 0
    if((1-p)==0)
      pr = 0 
    var prr = p1*(pr+pl)
    res += prr
    
   
  }

  return (this.ht - res);
  }

  //排序
  sortData(data,ht,label){
    var dataSet = data
    var dataLabel = label
    var numberOfCharacteristicType = this.numberOfCharacteristic
    var temp = 0
    // var datat = this.datat
    var datat = this.getdatat(data)
    for(let i = 0 ; i < ht.length-1; i++){
      for(let k = i+1 ; k < ht.length; k++){
        if(ht[k]>ht[i]){
          temp = ht[k]
          ht[k] = ht[i]
          ht[i] = temp
          temp = dataLabel[k]
          dataLabel[k] = dataLabel[i]
          dataLabel[i] = temp
          // temp = numberOfCharacteristicType[k]
          // numberOfCharacteristicType[k] = numberOfCharacteristicType[i]
          // numberOfCharacteristicType[i] = temp

          for(let a = 0 ;a<dataSet.length;a++){
            
              temp = dataSet[a][k]
              dataSet[a][k] = dataSet[a][i]
              dataSet[a][i] = temp
          }
          for(let a = 0 ;a<datat[0].length;a++){
            
            temp = datat[k][a]
            datat[k][a] = datat[i][a]
            datat[i][a] = temp
        }
        }
      }
    }
    var hash = this.getnoreapeatfeature(datat)
    return new DataOb(dataSet,dataLabel,hash)
    //console.log(this.createTree(dataLabel,dataSet,datat,null));
    //this.createTree(dataLabel,dataSet,numberOfCharacteristicType,datat)
    
  }

  getdatat(dataset){
    var datat= Array.from(Array(dataset[0].length-1), () => new Array(dataset.length));
    var linemax = dataset.length //15
    var rowmax  = dataset[0].length-1 // 4
    for(let row = 0;row<rowmax;row++){//4
      for(let line = 0;line<linemax;line++){//15
        datat[row][line] = dataset[line][row]
      }
    }
    return datat;

  }

  //分割数据集 旧版只能分俩叉 
  dealdataold(dataset,label){
    var datat = this.getdatat(dataset)
    var hash = this.getnoreapeatfeature(datat)
    var flag =0
    var tnum =0
    var fnum =0
    var snum =0
    for(var i = 0; i < hash.length; i++) {
      for(var a = 0; a < dataset.length; a++) {
        if(dataset[a][0]==hash[i]&&dataset[a].slice(-1)=="同意"){
          tnum++
        } 
        if(dataset[a][0]==hash[i]&&dataset[a].slice(-1)=="拒绝"){
          fnum++
        } 
        if(dataset[a][0]==hash[i]){
          snum++
        }
          
        }
      if(tnum==snum){//全是左支  
        console.log('全是左支');
        flag = 1
        
          for(var a = 0; a < dataset.length; a++) {
            if(dataset[a][0]=='有'&&dataset[a].slice(-1)=="同意"){
              dataset.splice(a,1)
              a--
            }}
        // return dataset
      }
      if(fnum==snum){//全是右支  
        console.log('全是右支');
        flag += 2
        
          for(var a = 0; a < dataset.length; a++) {
            if(dataset[a][0]=='没有'&&dataset[a].slice(-1)=="拒绝"){
              dataset.splice(a,1)
              a--
            }}
       

        // return dataset
      }
      tnum =0
      fnum =0
      snum =0
      
  }
  var split =0
  switch(flag){
    case 0:
      split =0
      break
    case 1:
      split =1
      break
    case 3:
      split =2
      break    
    }
  return new datasetandsplit(dataset,split)
  // return dataset
  }

  //分割数据集  新版花洒数据集
  dealdata(dataset,label){
    var datat = this.getdatat(dataset)
    var hash = this.getnoreapeatfeature(datat)
    var flag =0
    var tnum =0
    var fnum =0
    var snum =0
    var tlist =[]
    var flist =[]
    for(var i = 0; i < hash.length; i++) {
      for(var a = 0; a < dataset.length; a++) {
        if(dataset[a][0]==hash[i]&&dataset[a].slice(-1)=="同意"){
          tnum++
          tlist.push(a)
        } 
        if(dataset[a][0]==hash[i]&&dataset[a].slice(-1)=="拒绝"){
          fnum++
          flist.push(a)
        } 
        if(dataset[a][0]==hash[i]){
          snum++
        }
          
        }
      if(tnum==snum){//全是左支  
        console.log('全是左支');
        flag = 1
        
        for(var a = 0; a < tlist.length; a++) {
          if(a!=0)
          tlist[a]=tlist[a]-a
          dataset.splice(tlist[a],1)
          
        }
        // return dataset
      }
      if(fnum==snum){//全是右支  
        console.log('全是右支');
        flag += 2
        
        for(var a = 0; a < flist.length; a++) {
          if(a!=0)
          flist[a]=flist[a]-a

          dataset.splice(flist[a],1)
        }
       

        // return dataset
      }

      tnum =0
      fnum =0
      snum =0
      tlist =[]
      flist =[]
      
  }
  var split =0
  switch(flag){
    case 0:
      split =0
      break
    case 1:
      split =1
      break
    case 3:
      split =2
      break    
    }
  return new datasetandsplit(dataset,split)
  // return dataset
  }



  //输出不重复特征
  getnoreapeatfeature(datat){
    var hash =[];
    for (var i = 0; i < datat[0].length; i++) {
      if(hash.indexOf(datat[0][i])==-1){
      hash.push(datat[0][i]);
      } 
    }
    return hash
  }

  //没什么卵用 createnewtree
  createTree(label,dataset,datat,tree){
    if(!label){
      return
    }
    var hash =[];
    var count=0
    var flag =0
    if(tree ==null){
      tree ={}
    }
    var tnum =0
    var fnum =0
    //datat[index]为所求特征的索引  hash为所求特征的属性值
    for (var i = 0; i < datat[0].length; i++) {
      if(hash.indexOf(datat[0][i])==-1){
      hash.push(datat[0][i]);
      } 
    }
    for(var i = 0; i < hash.length; i++) {
      for(var a = 0; a < dataset.length; a++) {
        if(dataset[a][0]===hash[i]&&dataset[a].slice(-1)=="同意"){
          tnum++
        }else
          fnum++
 
        }
      if(tnum==0){//全否右支
        tree['domelt'] = label[0]
        tree['right'] = hash[1]
        flag = 1
        return tree
      }
      if(fnum==0){//全是左支
        tree['domelt'] = label[0]
        tree['left'] = hash[0]
        flag = 1
        return tree
      }
      hash.shift()
      
  }
      //剔除hash和dataset，datat
      label.shift()
      for(let a =0;a<dataset;a++){
        dataset[a].shift()
      }
      datat.splice(0,1)
      //不是全否全是，进入递归
      if(flag!=1){
        for(var a = 0; a < label.length; a++)
          return this.createTree(label,dataset,datat,tree)
     }
    }


  //计算特征种类  返回数组  没用
  numberOfCharacteristicType(data){
    var datat= Array.from(Array(data[0].length-1), () => new Array(data.length));
    var res = []
    var linemax = data.length //15
    var rowmax  = data[0].length-1 // 4
    for(let row = 0;row<rowmax;row++){//4
      for(let line = 0;line<linemax;line++){//15
        datat[row][line] = data[line][row]
      }
    }
    this.datat= datat;
    for(let row = 0;row<rowmax;row++){//4
      var ndata = []
      for(let line = 0;line<linemax;line++){//15
      
        ndata.push(datat[row][line])
      }
      var count = 0;
		  for(let i = 0;i<ndata.length;i++) {//判断0到N-1这些元素是不是重复的
			  for(let j = i+1;j<ndata.length;j++) {
				if(ndata[j]==ndata[i]) {
					//a[i]是重复元素
          count++;
					break;
				}
			}
      
		}
		res.push( data.length-count);
    }
  
    return res
  }

  //显示Train

  displayTrain(){
    var dataSetr = DefaultValue.TrainDS.dataSet
    var labelr = DefaultValue.TrainDS.label
    var dataSet =  Array.from(Array(dataSetr.length), () => new Array(dataSetr[0].length));
    var label =  Array(labelr.length)
    for(let a=0;a<dataSet.length;a++){
      for(let b =0 ;b<dataSet[a].length;b++){
        dataSet[a][b] = dataSetr[a][b]
      }
    }
    for(let a= 0;a<label.length;a++)
      label[a] = labelr[a] 
        var trainLog = []
        var label_=[]
        for(var i=0;i<label.length;i++){
          label_.push("\\textbf{"+label[i]+"}")
        }
    
        trainLog.push(label_)
    
        for(var j=0;j<dataSet.length;j++){
          var data_=[]
          for(var k=0;k<dataSet[j].length;k++){
            data_.push("\\textbf{"+dataSet[j][k]+"}")
          }
          trainLog.push(data_)
        }
        if(this.domTrain) this.domTrain.innerHTML = '';
        showMathStringInTable(this.domTrain, trainLog);
    
      }

  loadModel(json){

    if(json.TrainDS){
      let tmp = JSON.parse(json.TrainDS);
      this.trainDS.T = toVector(tmp.T, true);
      console.log(this.trainDS);
    }

    if(json.Model){
      console.log(1);
      let tmp = JSON.parse(json.Model);
      let node = new KDNode();
      node.copyFromJSON(tmp.node, true);
      this.model.node = node;
    }

    if(json.TestDS){
      this.testDS.T = toVector(json.TestDS.T, true);
    }
  }


  displayModel(){
    var dataSetr = DefaultValue.TrainDS.dataSet
    var labelr = DefaultValue.TrainDS.label
    var dataSet =  Array.from(Array(dataSetr.length), () => new Array(dataSetr[0].length));
    var label =  Array(labelr.length)
    for(let a=0;a<dataSet.length;a++){
      for(let b =0 ;b<dataSet[a].length;b++){
        dataSet[a][b] = dataSetr[a][b]
      }
    }
    for(let a= 0;a<label.length;a++)
      label[a] = labelr[a] 
    var root = new treeElement() 
    let node = this.model.node;
    
    if(this.figure){
      let range = new NodeRange([[this.figure.xMin, this.figure.xMax + 1],[this.figure.yMin,this.figure.yMax + 1]]);

      calcNodeRange(node, range);
      // 绘制分割线
      this.drawSplitLine(node);
      this.figure.update();
    }

    if(this.domModel) this.domModel.innerHTML = '';
    drawDecisionTree(this.domModel, this.outputMap(this.createNewTree(root,dataSet,label)));
    
  }


  displayTest(){
    const T = this.testDS.T;
    const R = this.testDS.R;
    let nearestPoints = [];
    let testTable = [];
    testTable.push([String.raw`\textbf{目标点}`, String.raw`\textbf{最近点}`, String.raw`\textbf{最近距离}`]);

    for(let i = 0, il = T.length; i < il; i++){
      testTable.push([]);
      testTable[i + 1][0] = T[i].toKatexString();
      if(R){
        testTable[i + 1][1] = R[i].nearest_point.toKatexString();
        // testTable[i + 1][2] = parseFloat(R[i].nearest_dist).fixed(2);
        testTable[i + 1][2] =String.raw`${R[i].nearest_dist.toFixed(2)}`;
        nearestPoints.push(R[i].nearest_point);
        this.figure.addCircle(T[i], R[i].nearest_point);
      }
    }

    if(this.domTest) this.domTest.innerHTML = '';
    showMathStringInTable(this.domTest, testTable);

  }

  test(){
    let result = [];
    let testLog = [];
    testLog.push([
      String.raw`\textbf{步骤}`, 
      String.raw`\textbf{特征}`, 
      String.raw`\textbf{信息增益}`,]);
    for(let a = 0 ;a<this.drawtree.length;a++){
      testLog.push([`${this.drawtree[a].step}`,`${this.drawtree[a].name}`,`${this.drawtree[a].entropy.toFixed(4)}`])
      if(a!=(this.drawtree.length-1)&&this.drawtree[a].step!=this.drawtree[a+1].step){
        testLog.push(['----','----','----']);
      }
    }

    if(this.domTable) this.domTable.innerHTML = '';
    showMathStringInTable(this.domTable, testLog);
    this.testDS.R = result;

    if(this.figure) this.figure.clearAll();
   
  }
  

}



//lastFeatureList没什么用 parent没什么用
class treeElement {
  constructor(featureList,lastFeatureList,name,parrent,children,split){
    this.name = name
    this.featureList = featureList
    this.lastFeatureList = lastFeatureList;
    this.split = split
  }
  setparrent(parrent){
    this.parrent = parrent
  }
  setchildren(children){
    this.children = children
  }
}

//用于控制下次循环的分支
//目前用flag控制split 0 1 2
class datasetandsplit{
  constructor(dataset,split){
    this.dataset = dataset
    this.split = split
  }
}


class DataOb{
  constructor(dataSet,label,hash){
    this.dataSet =dataSet
    this.label = label
    this.hash = hash
  }
}

class drawlist{
  constructor(name,entropy,step){
    this.name = name
    this.entropy = entropy
    this.step = step
  }
}
export default DecisionTreeID3;